#pragma once
#include "requestor.hpp"

namespace qiangsir
{
    namespace client
    {

        // 提供服务注册
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr& requestor):_requestor(requestor) {}
            bool registerMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                // 组织请求
                auto msg_req = BaseMessageFactory::create<ServiceRequest>();
                msg_req->setHost(host);
                msg_req->setType(MType::REQ_SERVERICE);
                msg_req->setId(UUID::uuid());
                msg_req->setMethod(method);
                msg_req->setOptype(ServiceOptype::SERVICE_REGISTRY);

                BaseMessage::ptr msg_resp;
                bool ret = _requestor->send(conn, msg_req, msg_resp);
                if (ret == false)
                {
                    ELOG("%s 服务注册失败", method.c_str());
                    return false;
                }
                ServiceResponse::ptr response = std::dynamic_pointer_cast<ServiceResponse>(msg_resp);
                if (response.get() == nullptr)
                {
                    ELOG("BaseMessage向下转换失败");
                    return false;
                }
                if (response->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务注册失败，原因: %s", errReason(response->rcode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        class MethodHosts
        {
        public:
            using ptr = std::shared_ptr<MethodHosts>;
            MethodHosts() : _index(0) {}
            MethodHosts(const std::vector<Address> &hosts)
                : _index(0), _hosts(hosts.begin(), hosts.end())
            {
            }
            void appendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }
            void removeHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = _index++ % _hosts.size();
                return _hosts[pos];
            }
            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            size_t _index;
            std::vector<Address> _hosts;
        };

        class Discovery
        {
        public:
            using ptr = std::shared_ptr<Discovery>;
            using OffLineCallBack = std::function<void(const Address&)>;
            Discovery(const Requestor::ptr& requestor,const OffLineCallBack& cb)
                    :_requestor(requestor),_cb(cb){}
            bool serviceDiscover(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end())
                    {
                        if (it->second->empty() == false)
                        {
                            host = it->second->chooseHost();
                            return true;
                        }
                    }
                }
                // 没有对应业务，自己构建服务发现请求
                auto msg_req = BaseMessageFactory::create<ServiceRequest>();
                msg_req->setId(UUID::uuid());
                msg_req->setMethod(method);
                msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY);
                msg_req->setType(MType::REQ_SERVERICE);
                BaseMessage::ptr msg_resp;
                bool ret = _requestor->send(conn, msg_req, msg_resp);
                if (ret == false)
                {
                    ELOG("服务发现失败");
                    return false;
                }
                ServiceResponse::ptr service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_resp);
                if (!service_rsp)
                {
                    ELOG("服务发现失败,响应向下转换失败");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务发现失败 %s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                // 创建主机管理
                std::unique_lock<std::mutex> _lock(_mutex);
                MethodHosts::ptr hosts = std::make_shared<MethodHosts>(service_rsp->host());
                if (hosts->empty())
                {
                    ELOG("%s 服务发现失败！没有能够提供服务的主机！", method.c_str());
                    return false;
                }
                host = hosts->chooseHost();
                _method_hosts[method] = hosts;
                return true;
            }

            void serviceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                std::unique_lock<std::mutex> _lock(_mutex);
                std::string method = msg->method();
                ServiceOptype optype = msg->optype();
                // 上线
                if (optype == ServiceOptype::SERVICE_ONLINE)
                {
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        MethodHosts::ptr host = std::make_shared<MethodHosts>();
                        host->appendHost(msg->host());
                        _method_hosts[method] = host;
                    }
                    else
                    {
                        it->second->appendHost(msg->host());
                    }
                }
                //下线
                else if (optype == ServiceOptype::SERVICE_OFFLINE)
                {
                      auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        return;
                    }
                    _cb(msg->host());
                    it->second->removeHost(msg->host());
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, MethodHosts::ptr> _method_hosts;
            OffLineCallBack _cb;
            Requestor::ptr _requestor;
        };
    }
}